package org.infodavid.common.model.impl;

import java.beans.Transient;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;

import org.infodavid.common.model.IGenericPropertiesMap;
import org.infodavid.common.model.IPerson;
import org.infodavid.common.model.IUser;
import org.infodavid.common.model.IUserGroup;
import org.infodavid.common.model.ModelFactory;
import org.infodavid.common.model.components.IEMail;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;

/**
 * The Class UserImpl.
 */
public class UserImpl extends AbstractNamedCommented<Long> implements IUser {

	/** serialVersionUID. */
	private static final long serialVersionUID = -1478066595640761600L;

	/** The connections count. */
	private long connectionsCount;

	/** The expiration date. */
	private DateTime expirationDate;

	/** The primary group. */
	private IUserGroup primaryGroup;

	/** The groups. */
	private Map<String,IUserGroup> groups;

	/** The last connection date. */
	private DateTime lastConnectionDate;

	/** The last ip. */
	private String lastIP;

	/** The locked. */
	private boolean locked;

	/** The password. */
	private String password;

	/** The person. */
	private IPerson person;

	/** The properties. */
	private IGenericPropertiesMap properties;

	/** The pseudonym. */
	private String pseudonym;

	/** The email. */
	private IEMail email;

	/** The time zone. */
	private DateTimeZone timeZone;

	/** The locale. */
	private Locale locale;

	/** The first name. */
	private String firstName;

	/** The last name. */
	private String lastName;

	/** The title. */
	private IPerson.EPersonTitle title;

	/** The highest role. */
	private IUserGroup.EUserRole highestRole;

	/**
	 * constructor.
	 * @throws Exception the exception
	 */
	protected UserImpl() throws Exception {
		super();
	}

	/**
	 * constructor.
	 * @param keyArg the key
	 * @throws Exception the exception
	 */
	protected UserImpl(final Long keyArg) throws Exception {
		super();

		setKey(keyArg);
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.model.IUser#addGroup(org.infodavid.common.model.IGroup)
	 */
	@Override
	public void addGroup(final IUserGroup value) {
		if (value != null) {
			getGroups().put(value.getName(), value);

			if (getPrimaryGroup() == null) {
				setPrimaryGroup(value);
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.model.impl.DataObjectImpl#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(final Object obj) {
		if (this == obj) {
			return true;
		}

		if (obj == null) {
			return false;
		}

		if (!(obj instanceof IUser)) {
			return false;
		}

		final IUser other = (IUser)obj;

		if (getName() == null) {
			if (other.getName() != null) {
				return false;
			}
		}
		else if (!getName().equals(other.getName())) {
			return false;
		}

		return true;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.auth.model.IUser#getConnectionsCount()
	 */
	@Override
	public long getConnectionsCount() {
		return connectionsCount;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.auth.model.IUser#getEmail()
	 */
	@Override
	public IEMail getEmail() {
		return email;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.auth.model.IUser#getExpirationDate()
	 */
	@Override
	public DateTime getExpirationDate() {
		return expirationDate;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.model.s.IUser#getFirstName()
	 */
	@Override
	public String getFirstName() {
		return getPerson() == null ? firstName : getPerson().getFirstName();
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.auth.model.IUser#getGroups()
	 */
	@Override
	public Map<String,IUserGroup> getGroups() {
		return groups;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.auth.model.IUser#getLastConnectionDate()
	 */
	@Override
	public DateTime getLastConnectionDate() {
		return lastConnectionDate;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.auth.model.IUser#getLastIP()
	 */
	@Override
	public String getLastIP() {
		return lastIP;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.model.s.IUser#getLastName()
	 */
	@Override
	public String getLastName() {
		return getPerson() == null ? lastName : getPerson().getName();
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.model.IUser#getLocale()
	 */
	@Override
	public Locale getLocale() {
		return locale;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.model.s.IUser#getLoginName()
	 */
	@Override
	@Transient
	public String getLoginName() {
		return getName();
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.auth.model.IUser#getPassword()
	 */
	@Override
	public String getPassword() {
		return password;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.auth.model.IUser#getPerson()
	 */
	@Override
	public IPerson getPerson() {
		return person;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.auth.model.IUser#getPrimaryGroup()
	 */
	@Override
	public IUserGroup getPrimaryGroup() {
		IUserGroup result = primaryGroup;

		if (result == null && getGroups() != null && !getGroups().isEmpty()) {
			result = getGroups().values().iterator().next();
		}

		return result;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.data.IGenericObject#getProperties()
	 */
	@Override
	public IGenericPropertiesMap getProperties() {
		return properties;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.auth.model.IUser#getPseudonym()
	 */
	@Override
	public String getPseudonym() {
		return pseudonym;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.model.IUser#getTimeZone()
	 */
	@Override
	public DateTimeZone getTimeZone() {
		return timeZone;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.model.s.IUser#getTitle()
	 */
	@Override
	public IPerson.EPersonTitle getTitle() {
		return getPerson() == null ? title : getPerson().getTitle();
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.model.impl.DataObjectImpl#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		final int result = 1;

		return prime * result + (getName() == null ? 0 : getName().hashCode());
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.model.IBasicDataObject#init()
	 */
	@Override
	public void init() throws Exception {
		super.init();

		connectionsCount = 0;
		expirationDate = null;
		groups = new HashMap<String,IUserGroup>();
		lastConnectionDate = null;
		lastIP = null;
		locked = false;
		password = null;
		person = null;
		properties = ModelFactory.getInstance().create(IGenericPropertiesMap.class);
		pseudonym = null;
		email = null;
		timeZone = null;
		locale = null;
		highestRole = IUserGroup.EUserRole.NORMAL;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.model.IUser#getHighestRole()
	 */
	@Override
	public IUserGroup.EUserRole getHighestRole() {
		return highestRole;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.model.IUser#setHighestRole(org.infodavid.common.model .s.IUserGroup
	 * .EUserRole)
	 */
	@Override
	public void setHighestRole(final IUserGroup.EUserRole value) {
		if (!equals(highestRole, value)) {
			final Object old = highestRole;
			highestRole = value;
			markModified(HIGHEST_ROLE, old, highestRole);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.auth.model.IUser#isExpired()
	 */
	@Override
	public boolean isExpired() {
		final DateTime expiration = getExpirationDate();

		return !(expiration == null || expiration.isAfterNow());
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.auth.model.IUser#isLocked()
	 */
	@Override
	public boolean isLocked() {
		return locked;
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.model.IUser#removeGroup(org.infodavid.common.model.IGroup )
	 */
	@Override
	public boolean removeGroup(final IUserGroup value) {
		return getGroups().remove(value.getName()) == value;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.auth.model.IUser#setConnectionsCount(long)
	 */
	@Override
	@SuppressWarnings("boxing")
	public void setConnectionsCount(final long value) {
		if (!equals(connectionsCount, value)) {
			final Object old = connectionsCount;
			connectionsCount = value;
			markModified(CONNECTIONS_COUNT, old, connectionsCount);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.auth.model.IUser#setEmail(com.infodavid.auth.model.IEMail)
	 */
	@Override
	public void setEmail(final IEMail value) {
		if (!equals(email, value)) {
			final Object old = email;
			email = value;
			markModified(EMAIL, old, email);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.auth.model.IUser#setExpirationDate(org.joda.time.DateTime)
	 */
	@Override
	public void setExpirationDate(final DateTime value) {
		if (!equals(expirationDate, value)) {
			final Object old = expirationDate;
			expirationDate = value;
			markModified(EXPIRATION_DATE, old, expirationDate);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.model.s.IUser#setFirstName(java.lang .String)
	 */
	@Override
	public void setFirstName(final String value) {
		if (!equals(firstName, value)) {
			final Object old = firstName;
			firstName = value;

			if (getPerson() != null) {
				getPerson().setFirstName(value);
			}

			markModified(FIRST_NAME, old, firstName);
		}
	}

	/**
	 * Sets the groups.
	 * @param values the groups
	 */
	protected void setGroups(final Map<String,IUserGroup> values) {
		final Object old = groups;
		groups = values;
		markModified(GROUPS, old, groups);

		if (getPrimaryGroup() == null && !(groups == null || groups.isEmpty())) {
			setPrimaryGroup(groups.values().iterator().next());
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.auth.model.IUser#setLastConnectionDate(org.joda.time.DateTime )
	 */
	@Override
	public void setLastConnectionDate(final DateTime value) {
		if (!equals(lastConnectionDate, value)) {
			final Object old = lastConnectionDate;
			lastConnectionDate = value;
			markModified(LAST_CONNECTION_DATE, old, lastConnectionDate);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.auth.model.IUser#setLastIP(java.lang.String)
	 */
	@Override
	public void setLastIP(final String value) {
		if (!equals(lastIP, value)) {
			final Object old = lastIP;
			lastIP = value;
			markModified(LAST_IP, old, lastIP);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.model.s.IUser#setLastName(java.lang .String)
	 */
	@Override
	public void setLastName(final String value) {
		if (!equals(lastName, value)) {
			final Object old = lastName;
			lastName = value;

			if (getPerson() != null) {
				getPerson().setName(value);
			}

			markModified(LAST_NAME, old, lastName);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.model.IUser#setLocale(java.util.Locale)
	 */
	@Override
	public void setLocale(final Locale value) {
		if (!equals(locale, value)) {
			final Object old = locale;
			locale = value;
			markModified(LOCALE, old, locale);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.auth.model.IUser#setLocked(boolean)
	 */
	@Override
	@SuppressWarnings("boxing")
	public void setLocked(final boolean value) {
		if (!equals(locked, value)) {
			final Object old = locked;
			locked = value;
			markModified(LOCKED, old, locked);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.model.s.IUser#setLoginName(java.lang .String)
	 */
	@Override
	public void setLoginName(final String value) {
		setName(value);
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.auth.model.IUser#setPassword(java.lang.String)
	 */
	@Override
	public void setPassword(final String value) {
		if (!equals(password, value)) {
			final Object old = password;
			password = value;
			markModified(PASSWORD, old, password);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.auth.model.IUser#setPerson(org.infodavid.common.model.IPerson)
	 */
	@Override
	public void setPerson(final IPerson value) {
		if (!equals(person, value)) {
			final Object old = person;
			person = value;

			if (value != null) {
				setFirstName(value.getFirstName());
				setLastName(value.getName());
				setTitle(value.getTitle());
			}

			markModified(PERSON, old, person);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.auth.model.IUser#setPrimaryGroup(com.infodavid.auth.model.IGroup )
	 */
	@Override
	public void setPrimaryGroup(final IUserGroup value) {
		if (!equals(primaryGroup, value)) {
			final Object old = getPrimaryGroup();
			primaryGroup = value;
			// add primary group to the collection of nassociated groups
			addGroup(value);
			markModified(PRIMARY_GROUP, old, value);
		}
	}

	/**
	 * Sets the properties.
	 * @param values the properties
	 */
	protected void setProperties(final IGenericPropertiesMap values) {
		final Object old = properties;
		properties = values;
		markModified(PROPERTIES, old, properties);
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.auth.model.IUser#setPseudonym(java.lang.String)
	 */
	@Override
	public void setPseudonym(final String value) {
		if (!equals(pseudonym, value)) {
			final Object old = pseudonym;
			pseudonym = value;
			markModified(PSEUDONYM, old, pseudonym);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.model.IUser#setTimeZone(java.lang.Byte)
	 */
	@Override
	public void setTimeZone(final DateTimeZone value) {
		if (!equals(timeZone, value)) {
			final Object old = timeZone;
			timeZone = value;
			markModified(TIME_ZONE, old, timeZone);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.model.entities.IPerson#setTitle(org.infodavid.common .model
	 * .entities.IPerson.EPersonTitle)
	 */
	@Override
	public void setTitle(final IPerson.EPersonTitle value) {
		if (!equals(title, value)) {
			final Object old = title;
			title = value;

			if (getPerson() != null) {
				getPerson().setTitle(value);
			}

			markModified(TITLE, old, title);
		}
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.impl.model.DataObjectImpl#toString(java.lang.StringBuffer )
	 */
	@Override
	protected StringBuffer toString(final StringBuffer builder) {
		super.toString(builder);

		builder.append("[connectionsCount=");
		builder.append(connectionsCount);
		builder.append(",expirationDate=");
		builder.append(expirationDate);
		builder.append(",primaryGroup=");

		if (primaryGroup == null) {
			builder.append("null");
		}
		else {
			builder.append(primaryGroup.getKey());
		}

		builder.append(",groups=");

		if (groups == null) {
			builder.append("null");
		}
		else {
			final Iterator<IUserGroup> ite = groups.values().iterator();

			if (ite.hasNext()) {
				builder.append(ite.next().getKey());

				while (ite.hasNext()) {
					builder.append(',');
					builder.append(ite.next().getKey());
				}
			}
		}

		builder.append(",lastConnectionDate=");
		builder.append(lastConnectionDate);
		builder.append(",lastIP=");
		builder.append(lastIP);
		builder.append(",locked=");
		builder.append(locked);
		builder.append(",password=");

		if (password == null) {
			builder.append("null");
		}
		else {
			builder.append(password.replaceAll(".", "*"));
		}

		builder.append(",person=");

		if (person == null) {
			builder.append("null");
		}
		else {
			builder.append(person.toString());
		}

		builder.append(",properties=");
		builder.append(properties);
		builder.append(",pseudonym=");
		builder.append(pseudonym);
		builder.append(",email=");
		builder.append(email);
		builder.append(",timeZone=");

		if (timeZone == null) {
			builder.append("null");
		}
		else {
			builder.append(timeZone.getID());
		}

		builder.append(",locale=");
		builder.append(locale);
		builder.append("]");

		return builder;
	}
}
